/*
	This is a part of the source code for Pro/DESKTOP.
	Copyright (C) 1998-1999 Parametric Technology Corporation.
	All rights reserved.
*/

// Designing a Gear 

#include <stdafx.h>
#include <math.h>

//Interface includes
#include "IApplication.h"
#include "IPartDocument.h"
#include "IGraphicDocument.h"
#include "IaWorkplane.h"
#include "IaSketch.h"
#include "IaLine.h"
#include "IzStraight.h"
#include "ISet.h"
#include "IzVector.h"
#include "IzPlane.h"
#include "IaGeometric.h"
#include "IaExtrusion.h"
#include "IHelm.h"

//Global Variables
IApplication *pdApp;

//==============================================================================================================
//Forward declarations of some convenience functions used by GearDesign()
HRESULT GetProDESKTOPApplication(IApplication **proDApp);
HRESULT GetActivePartWorkplaneSketch(IPartDocument **activePart, IGraphicDocument **activeGraphicDoc, IWorkplane **activeWorkplane, ISketch **activeSketch);
HRESULT CommitToProDESKTOP(CString str,BOOL bl = FALSE);

//macro defintions
#define CHECK_RETURN_STATUS(status) \
	if (FAILED(status))
		return E_FAIL;

#define CAST(cls, obj) \
	QueryInterface<cls>(obj,IID_##cls) \

template <class T> 
T *QueryInterface(IDispatch *disp,GUID iid)
{
	void *pUnk;
	HRESULT status = disp->QueryInterface(iid,(void **)&pUnk);
	if (FAILED(status)) { 
		ASSERT(FALSE);
		AfxThrowOleException(status);
	} 
	disp->Release();
	return (T *)pUnk;

}

#define GetCLASS(cls) \
	GetFormalClass<I##cls##Class>(IID_I##cls##Class, #cls) \

template <class T>
T *GetFormalClass(GUID iid, CString clsName)
{
	IDispatch *disp = NULL;
	HRESULT status = pdApp->GetClass(clsName.AllocSysString(), &disp);

	T *thisCls = NULL;
	status = disp->QueryInterface(iid, (void **)&thisCls);
	if(status || !thisCls) {
		AfxMessageBox("QueryInterface Failed");
		return NULL;
	}
	return thisCls;
}

//==============================================================================================================

HRESULT GearDesign()
{
	HRESULT status = CONV_SUCCESS;

	IApplicationAObject *appAObject = NULL;
	ISet *localObjectSet = NULL;
	IPartDocument *activePart = NULL;
	IWorkplane *activeWorkplane = NULL;
	ISketch *activeSketch = NULL;
	IGraphicDocument *activeGraphicDoc = NULL;
	IPartOthers *part1 = NULL;
	IApplication *pdApplication = NULL;
	IApplicationZObject *appZObject;
	IVector *prLinePoint1 = NULL;
	IVector *prLinePoint2 = NULL;
	IVector *prLinePoint3 = NULL;
	IVector *prLinePoint4 = NULL;

	IVector *pCenterPoint = NULL;
	IPlane *pPlane = NULL;
	IDirection *pNormal = NULL;
	IVector *pVeryStartPoint = NULL;
	ILine *pLine1 = NULL;
	
	IVector *pEndpoint = NULL;
	IVector *psHole1 = NULL;
	IVector *psHole2 = NULL;
	IVector *psHole3 = NULL;
	IVector *psHole4 = NULL;

	ILine *pLine = NULL;

	IVector *l_vector1 = NULL;
	IVector *l_vector2 = NULL;
	IVector *l_vector3 = NULL;
	IVector *l_vector4 = NULL;

	IBasicStraight *pCurve1 = NULL;
	IBasicCircularArc *pBasicCircularArc = NULL;

	IPlane *l_zPlane = NULL;
	IGeometric *localGeometric = NULL;
	IExtrusion *l_extrusion = NULL;
	IDesign *pDesign = NULL;
	IGeometry *pGeometry = NULL;

	IPartDocument *newPart = NULL;
    
	// Get the application
	status = GetProDESKTOPApplication(&pdApplication);
	CHECK_RETURN_STATUS(status)
	pdApp = pdApplication;

	status = pdApplication->NewPart(&newPart);
	CHECK_RETURN_STATUS(status)
	
	status = GetActivePartWorkplaneSketch(&activePart, &activeGraphicDoc, &activeWorkplane, &activeSketch);
	CHECK_RETURN_STATUS(status)

	part1 = CAST(IPartOthers, activePart);

	// STEPI Get the input parameter
	double PCD = 0.18;
	double Diff = 0.02;
	double angle = 20;
	double BCD = PCD - Diff;
	double OCD = PCD + Diff;
	int NoOfTeeth = 18;
	double ToothWidth = (2 *(22 / 7) *PCD) / (2 *NoOfTeeth);

	// To find the X & Y coordinates on the Base Circle
	// The line (on the right) equation is y = Tan(angle+90)x + (PCD - Tan(angle+90)*Toothwidth/2)
	// Base Circle Equation is SQR(x) + SQR(y) = SQR(BCD) (SQR is for square)
	// If we solve the above two equations t ofind the point of intersection of the
	// line with the circle we get a quadratic equation of degree two (Line intersects
	// the circle at two places)
	// SQR(x) *(1+SQR(tan(angle+90))) + 2*tan(angle+90)*CONST*x + (SQR(CONST) - SQR(BCD)) = 0
	//                   A                          B                     C
	// Where CONST = PCD - tan(angle+90)*(toothwidth/2)
	// Now we will solve the values of x & Y using the equation
	// x = (-B + SQRT(SQR(b)- 4*A*C) / 2*A
	// and by substituting the value of x in any of the above equation we will get the value of y

	double A1, B1, C1;
	double AngleinRad1 = tan(((angle + 90) *22) / (7 *180));
	double LineConst = PCD - (AngleinRad1 *ToothWidth / 2);

	// To find the point of intersection of the right line with the Base circle
	A1 = 1 + (AngleinRad1 *AngleinRad1);
	B1 = 2 *AngleinRad1 *LineConst;
	C1 = (LineConst *LineConst) - (BCD *BCD);

	double xrBCD;
	double yrBCD;
	double Disc1 = sqrt(B1 *B1 - 4 *A1 *C1);

	xrBCD = ((-1 *B1) - Disc1) / (2 *A1);
	yrBCD = AngleinRad1 *xrBCD + LineConst;

	// To find the point of intersection of the right line with the Outer circle
	double A2 = 1 + (AngleinRad1 *AngleinRad1);
	double B2 = 2 *AngleinRad1 *LineConst;
	double C2 = (LineConst *LineConst) - (OCD *OCD);
	double xrOCD;
	double yrOCD;
	double Disc2 = sqrt(B2 *B2 - 4 *A2 *C2);

	xrOCD = ((-1 *B2) - Disc2) / (2 *A2);
	yrOCD = AngleinRad1 *xrOCD + LineConst;

	// Create the Base Circle point
	status = GetCLASS("Vector")->CreateVector(xrBCD, yrBCD, 0, &prLinePoint1);
	CHECK_RETURN_STATUS(status)

	// Create the Outer Circle point
	status = GetCLASS("Vector")->CreateVector(xrOCD, yrOCD, 0, &prLinePoint2);
	CHECK_RETURN_STATUS(status)

	// Create the left line
	double A3;
	double B3;
	double C3;
	double AngleinRad2 = tan(((90 - angle) *22) / (7 *180));
	double LineConst2 = PCD + (AngleinRad2 *ToothWidth / 2);

	// To find the point of intersection of the right line with the Base circle
	A3 = 1 + (AngleinRad2 *AngleinRad2);
	B3 = 2 *AngleinRad2 *LineConst2;
	C3 = (LineConst2 *LineConst2) - (BCD *BCD);

	double xlBCD;
	double ylBCD;
	double Disc3 = sqrt(B3 *B3 - 4 *A3 *C3);

	xlBCD = ((-1 *B3) + Disc3) / (2 *A3);
	ylBCD = AngleinRad2 *xlBCD + LineConst2;

	// To find the point of intersection of the right line with the Outer circle
	double A4 = 1 + (AngleinRad2 *AngleinRad2);
	double B4 = 2 *AngleinRad2 *LineConst2;
	double C4 = (LineConst2 *LineConst2) - (OCD *OCD);

	double xlOCD;
	double ylOCD;
	double Disc4 = sqrt(B4 *B4 - 4 *A4 *C4);

	xlOCD = ((-1 *B4) + Disc4) / (2 *A4);
	ylOCD = AngleinRad2 *xlOCD + LineConst2;

	// Create the Base Circle point
	status = GetCLASS("Vector")->CreateVector(xlBCD, ylBCD, 0, &prLinePoint3);
	CHECK_RETURN_STATUS(status)

	// Create the Outer Circle point
	status = GetCLASS("Vector")->CreateVector(xlOCD, ylOCD, 0, &prLinePoint4);
	CHECK_RETURN_STATUS(status)

	// Step 3 :-Create a Circle with center ar 0,0,0 and radius of BCD on
	// the active sketch

	// Set Center
	status = GetCLASS("Vector")->CreateVector(0, 0, 0, &pCenterPoint);
	CHECK_RETURN_STATUS(status)

	// Set circle attributes
	localGeometric = CAST(IGeometric, activeWorkplane);
	
	status = localGeometric->GetGeometry (&pGeometry);
	CHECK_RETURN_STATUS(status)

	l_zPlane = CAST(IPlane, pGeometry);

	status = l_zPlane->GetNormal(&pNormal);
	CHECK_RETURN_STATUS(status)

	// Store the very first point. Useful to draw the last are
	pVeryStartPoint = prLinePoint1;

	double RotateAngle = ((360. / NoOfTeeth) *(22. / 7.)) / 180.;

	for(int i = 1; i <= NoOfTeeth;i++) {

		// Create the Right line
		status = GetCLASS("BasicStraight") -> CreateBasicStraightTwoPoints(prLinePoint1, prLinePoint2, &pCurve1);
		CHECK_RETURN_STATUS(status)

		status = activeSketch->CreateLine(CAST(ICurve, pCurve1), &pLine1);
		CHECK_RETURN_STATUS(status)

		// Create the top line
		status = GetCLASS("BasicStraight") ->CreateBasicStraightTwoPoints(prLinePoint2, prLinePoint4, &pCurve1);
		CHECK_RETURN_STATUS(status)

		status = activeSketch->CreateLine(CAST(ICurve, pCurve1), &pLine1);
		CHECK_RETURN_STATUS(status)

		// Create the left line
		status = GetCLASS("BasicStraight") ->CreateBasicStraightTwoPoints(prLinePoint4, prLinePoint3, &pCurve1);
		CHECK_RETURN_STATUS(status)

		status = activeSketch->CreateLine(CAST(ICurve, pCurve1), &pLine1);
		CHECK_RETURN_STATUS(status)

		// Set Startpoint = rLinePoint1
		pEndpoint = prLinePoint3;

		l_vector1 = CAST(IVector, prLinePoint1);
		l_vector2 = CAST(IVector, prLinePoint2);
		l_vector3 = CAST(IVector, prLinePoint3);
		l_vector4 = CAST(IVector, prLinePoint4);

		status = l_vector1->Rotate(pNormal, RotateAngle, &prLinePoint1);
		CHECK_RETURN_STATUS(status)

		status = l_vector2->Rotate(pNormal, RotateAngle, &prLinePoint2);
		CHECK_RETURN_STATUS(status)

		status = l_vector3->Rotate(pNormal, RotateAngle, &prLinePoint3);
		CHECK_RETURN_STATUS(status)

		status = l_vector4->Rotate(pNormal, RotateAngle, &prLinePoint4);
		CHECK_RETURN_STATUS(status)

		if(i == 18) {
		  status = GetCLASS("BasicCircularArc") ->CreateBasicCircularArc(pCenterPoint, pNormal, BCD, pEndpoint, pVeryStartPoint, &pBasicCircularArc);
		  CHECK_RETURN_STATUS(status)
		}
		else {
		   status = GetCLASS("BasicCircularArc") ->CreateBasicCircularArc(pCenterPoint, pNormal, BCD, pEndpoint, prLinePoint1, &pBasicCircularArc);
		   CHECK_RETURN_STATUS(status)
		}
		// Create the circle
		status = activeSketch->CreateLine(CAST(ICurve, pBasicCircularArc), &pLine);
		CHECK_RETURN_STATUS(status)

	} // for  loop

	//To create the shaft hole
	double xhole = sqrt(0.05 *0.05 - 0.01 *0.01);

	status = GetCLASS("Vector") ->CreateVector(xhole, -0.01, 0, &psHole1);
	CHECK_RETURN_STATUS(status)

	status = GetCLASS("Vector") ->CreateVector(xhole + 0.01, -0.01, 0, &psHole2);
	CHECK_RETURN_STATUS(status)

	status = GetCLASS("Vector") ->CreateVector(xhole + 0.01, 0.01, 0, &psHole3);
	CHECK_RETURN_STATUS(status)

	status = GetCLASS("Vector") ->CreateVector(xhole, 0.01, 0, &psHole4);
	CHECK_RETURN_STATUS(status)

	status = GetCLASS("BasicCircularArc") ->CreateBasicCircularArc(pCenterPoint, pNormal, 0.05, psHole4, psHole1, &pBasicCircularArc);
	CHECK_RETURN_STATUS(status)

	status = activeSketch->CreateLine(CAST(ICurve, pBasicCircularArc), &pLine);
	CHECK_RETURN_STATUS(status)

	status = GetCLASS("BasicStraight") ->CreateBasicStraightTwoPoints(psHole1, psHole2, &pCurve1);
	CHECK_RETURN_STATUS(status)

	status = activeSketch->CreateLine(CAST(ICurve, pCurve1), &pLine1);
	CHECK_RETURN_STATUS(status)

	status = GetCLASS("BasicStraight") ->CreateBasicStraightTwoPoints(psHole2, psHole3, &pCurve1);
	CHECK_RETURN_STATUS(status)

	status = activeSketch->CreateLine(CAST(ICurve, pCurve1), &pLine1);
	CHECK_RETURN_STATUS(status)

	status = GetCLASS("BasicStraight") ->CreateBasicStraightTwoPoints(psHole3, psHole4, &pCurve1);
	CHECK_RETURN_STATUS(status)

	status = activeSketch->CreateLine(CAST(ICurve, pCurve1), &pLine1);
	CHECK_RETURN_STATUS(status)

	status = activePart->GetDesign(&pDesign);
	CHECK_RETURN_STATUS(status)

	status = GetCLASS("Extrusion")->CreateExtrusion(pDesign, activeSketch, 0.05, 0, 0, 1, 1, 0, &l_extrusion);
	CHECK_RETURN_STATUS(status)

	status = CommitToProDESKTOP("CreateExtrusion");
	CHECK_RETURN_STATUS(status)

	status = activePart->UpdateDesign();
	CHECK_RETURN_STATUS(status)
	
	status = CommitToProDESKTOP("UpdateDesign");
	CHECK_RETURN_STATUS(status)

	return NOERROR;
}

//===========================================================================================================

HRESULT GetProDESKTOPApplication(IApplication **proDApp)
{
	HRESULT status = NOERROR;
	IApplication *app = NULL;

	if (pdApp = NULL) {
		status = CoCreateInstance(CLSID_ProDESKTOP, NULL, CLSCTX_SERVER, IID_IApplication, (void**)&app);
		CHECK_RETURN_STATUS(status)

		status = (app)->SetVisible(TRUE);
		CHECK_RETURN_STATUS(status)
		*proDApp = app;
	}
	else 
		proDApp = pdApp;

	return NOERROR;
}

HRESULT GetActivePartWorkplaneSketch(IPartDocument **activePart,
									 IGraphicDocument **activeGraphicDoc,
									 IWorkplane **activeWorkplane,
									 ISketch **activeSketch)
{
/* Queries the  PartDocument, Workplane , and Sketch Interfaces and returns the active Part, active Workplane, active Sketch. */

	HRESULT status = NOERROR;

	IPartDocument *part = NULL;
	IWorkplane *workplane = NULL;

	ISketch * sketch = NULL;
	IApplication *app = NULL;

	// Get the application
	status = GetProDESKTOPApplication(&app) ;
	CHECK_RETURN_STATUS(status)

	status = app->GetActiveDoc(activeGraphicDoc);
	CHECK_RETURN_STATUS(status)

	*activePart = CAST(IPartDocument, *activeGraphicDoc);

	// Get active workplane
	status = (*activeGraphicDoc)->GetActiveWorkplane(activeWorkplane);
	CHECK_RETURN_STATUS(status)

	// Get active sketch
	status = (*activeGraphicDoc)->GetActiveSketch(activeSketch);
	CHECK_RETURN_STATUS(status)

	return NOERROR;
}

HRESULT CommitToProDESKTOP(CString str,BOOL bl)
{
	HRESULT status = CONV_SUCCESS;

	// Take the helm
	IHelm *pHelm = NULL;
	status = GetProDESKTOPApplication(&app);
	CHECK_RETURN_STATUS(status)

	status = app->TakeHelm(&pHelm);
	CHECK_RETURN_STATUS(status)

	// Committing the operation
	status = pHelm->CommitCalls(str.AllocSysString(), bl);
	CHECK_RETURN_STATUS(status)

	pHelm->Release();

	return NOERROR;
}
